﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Basmus.Values.Attributive
{
    public class ContextService : IContextService
    {
        private readonly ConcurrentDictionary<Type, ModelContext[]> _modelContexts =
            new ConcurrentDictionary<Type, ModelContext[]>();

        public IEnumerable<ModelContext> GetModelContexts(Type targetType)
        {
            var modelContexts = _modelContexts.GetOrAdd(targetType, BuildModelContexts);
            return modelContexts;
        }

        private static ModelContext[] BuildModelContexts(Type targetType)
        {
            var typeKeys = targetType
                .GetCustomAttributes(false)
                .OfType<IScheme>()
                .Select(x => x.Key);
            var propertyKeys = targetType
                .GetProperties()
                .SelectMany(x => x.GetCustomAttributes(false).Cast<IScheme>())
                .Select(x => x.Key);
            var contexts = typeKeys
                .Concat(propertyKeys)
                .Concat(new[] {SchemeKey.Default})
                .Distinct()
                .Select(x => new ModelContext(targetType, x))
                .ToArray();
            return contexts;
        }

        private readonly ConcurrentDictionary<ModelContext, ValuesContext[]> _valuesContexts =
            new ConcurrentDictionary<ModelContext, ValuesContext[]>();

        public IEnumerable<ValuesContext> GetValuesContexts(ModelContext modelContext)
        {
            var valuesContexts = _valuesContexts.GetOrAdd(modelContext, BuildValueContexts);
            return valuesContexts;
        }

        private static ValuesContext[] BuildValueContexts(ModelContext modelContext)
        {
            var valuesContexts = modelContext
                .TargetType
                .GetProperties()
                .Select(propertyInfo =>
                            {
                                var name = propertyInfo
                                    .GetCustomAttributes(typeof (NameAttribute), false)
                                    .Cast<NameAttribute>()
                                    .Where(nameAttr => nameAttr.Key == modelContext.SchemeKey)
                                    .Select(nameAttr => nameAttr.Name)
                                    .FirstOrDefault();
                                if (name == null)
                                {
                                    name = propertyInfo
                                        .GetCustomAttributes(typeof (NameAttribute), false)
                                        .Cast<NameAttribute>()
                                        .Where(nameAttr => nameAttr.Key == SchemeKey.Default)
                                        .Select(nameAttr => nameAttr.Name)
                                        .FirstOrDefault();
                                }
                                if (name == null)
                                {
                                    name = propertyInfo.Name;
                                }
                                var valuesContext = new ValuesContext(modelContext, name);
                                return valuesContext;
                            })
                .ToArray();
            return valuesContexts;
        }
    }
}